home *** CD-ROM | disk | FTP | other *** search
/ Cream of the Crop 26 / Cream of the Crop 26.iso / os2 / octa209s.zip / octave-2.09 / src / pager.cc < prev    next >
C/C++ Source or Header  |  1997-05-26  |  10KB  |  514 lines

  1. /*
  2.  
  3. Copyright (C) 1996 John W. Eaton
  4.  
  5. This file is part of Octave.
  6.  
  7. Octave is free software; you can redistribute it and/or modify it
  8. under the terms of the GNU General Public License as published by the
  9. Free Software Foundation; either version 2, or (at your option) any
  10. later version.
  11.  
  12. Octave is distributed in the hope that it will be useful, but WITHOUT
  13. ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  14. FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  15. for more details.
  16.  
  17. You should have received a copy of the GNU General Public License
  18. along with Octave; see the file COPYING.  If not, write to the Free
  19. Software Foundation, 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
  20.  
  21. */
  22.  
  23. /* Modified by Klaus Gebhardt, 1996 */
  24.  
  25. #ifdef HAVE_CONFIG_H
  26. #include <config.h>
  27. #endif
  28.  
  29. #include <csignal>
  30.  
  31. #include <string>
  32. #include <fstream.h>
  33.  
  34. #include "oct-term.h"
  35.  
  36. #include "procstream.h"
  37.  
  38. #include <defaults.h>
  39. #include "defun.h"
  40. #include "error.h"
  41. #include "gripes.h"
  42. #include "help.h"
  43. #include "input.h"
  44. #include "oct-obj.h"
  45. #include "pager.h"
  46. #include "sighandlers.h"
  47. #include "unwind-prot.h"
  48. #include "utils.h"
  49. #include "variables.h"
  50.  
  51. static pid_t octave_pager_pid = -1;
  52.  
  53. // Our actual connection to the external pager.
  54. static oprocstream *external_pager = 0;
  55.  
  56. // Nonzero means we write to the diary file.
  57. static int write_to_diary_file = 0;
  58.  
  59. // The name of the current diary file.
  60. static string diary_file;
  61.  
  62. // The diary file.
  63. static ofstream external_diary_file;
  64.  
  65. // The shell command to run as the pager.
  66. static string Vpager_binary;
  67.  
  68. // TRUE means that if output is going to the pager, it is sent as soon
  69. // as it is available.  Otherwise, it is buffered and only sent to the
  70. // pager when it is time to print another prompt.
  71. static bool Vpage_output_immediately;
  72.  
  73. // TRUE means all output intended for the screen should be passed
  74. // through the pager.
  75. static bool Vpage_screen_output;
  76.  
  77. // Only one pager can be active at once, so having these at file
  78. // scope should be ok.
  79. static octave_interrupt_handler saved_interrupt_handler;
  80. static bool interrupt_handler_saved = false;
  81.  
  82. static int really_flush_to_pager = 0;
  83.  
  84. static int flushing_output_to_pager = 0;
  85.  
  86. static void
  87. clear_external_pager (void)
  88. {
  89.   octave_child_list::remove (octave_pager_pid);
  90.  
  91.   octave_pager_pid = -1;
  92.  
  93.   delete external_pager;
  94.   external_pager = 0;
  95.  
  96.   if (interrupt_handler_saved)
  97.     {
  98.       octave_set_interrupt_handler (saved_interrupt_handler);
  99.       interrupt_handler_saved = false;
  100.     }
  101. }
  102.  
  103. static void
  104. pager_death_handler (pid_t pid, int status)
  105. {
  106.   if (pid > 0)
  107.     {
  108.       if (WIFEXITED (status) || WIFSIGNALLED (status))
  109.     {
  110.       // Avoid warning() or error(), since that will put us back in
  111.       // the pager, which would be bad news.
  112.  
  113.       cerr << "warning: connection to external pager (pid = "
  114.            << pid << ") lost --\n"
  115.            << "warning: attempting to finish pending computations...\n";
  116.     }
  117.     }
  118. }
  119.  
  120. static void
  121. do_sync (const char *msg, bool bypass_pager)
  122. {
  123.   if (msg && *msg)
  124.     {
  125.       if (bypass_pager)
  126.     {
  127.       cout << msg;
  128.       cout.flush ();
  129.     }
  130.       else
  131.     {
  132.       if (! external_pager)
  133.         {
  134.           string pgr = Vpager_binary;
  135.  
  136.           if (! pgr.empty ())
  137.         {
  138.           saved_interrupt_handler = octave_ignore_interrupts ();
  139.           interrupt_handler_saved = true;
  140.  
  141.           external_pager = new oprocstream (pgr.c_str ());
  142.  
  143.           if (external_pager)
  144.             {
  145.               octave_pager_pid = external_pager->pid ();
  146.  
  147.               octave_child_list::insert (octave_pager_pid,
  148.                          pager_death_handler);
  149.             }
  150.         }
  151.         }
  152.  
  153.       if (external_pager)
  154.         {
  155.           if (octave_pager_pid > 0 && external_pager->good ())
  156.         {
  157.           *external_pager << msg;
  158.  
  159.           // These checks are needed if a signal handler
  160.           // invoked since the last set of checks attempts
  161.           // to flush output and then returns
  162.  
  163.           if (octave_pager_pid > 0
  164.               && external_pager
  165.               && external_pager->good ())
  166.             external_pager->flush ();
  167.         }
  168.           else
  169.         {
  170.           // We had a pager, but it must have died.  Restore
  171.           // the interrupt state so we can escape back to the
  172.           // prompt if there are lots of computations pending.
  173.  
  174.           if (interrupt_handler_saved)
  175.             {
  176.               octave_set_interrupt_handler (saved_interrupt_handler);
  177.               interrupt_handler_saved = false;
  178.             }
  179.         }
  180.         }
  181.       else
  182.         {
  183.           cout << msg;
  184.           cout.flush ();
  185.         }
  186.     }
  187.     }
  188. }
  189.  
  190. static bool
  191. more_than_a_screenful (const char *s)
  192. {
  193.   if (s)
  194.     {
  195.       int available_rows = terminal_rows () - 2;
  196.  
  197.       int count = 0;
  198.  
  199.       char c;
  200.  
  201.       while ((c = *s++) != '\0')
  202.     if (c == '\n')
  203.       {
  204.         count++;
  205.  
  206.         if (count > available_rows)
  207.           return true;
  208.       }
  209.     }
  210.  
  211.   return false;
  212. }
  213.  
  214. int
  215. octave_pager_buf::sync (void)
  216. {
  217.   if ((! interactive || really_forced_interactive)
  218.       || really_flush_to_pager
  219.       || (Vpage_screen_output && Vpage_output_immediately)
  220.       || ! Vpage_screen_output)
  221.     {
  222.       sputc ('\0');
  223.  
  224.       char *buf = eback ();
  225.  
  226.       bool bypass_pager = ((! interactive || really_forced_interactive)
  227.                || ! Vpage_screen_output
  228.                || (really_flush_to_pager
  229.                    && Vpage_screen_output
  230.                    && ! Vpage_output_immediately
  231.                    && ! more_than_a_screenful (buf)));
  232.  
  233.       seekoff (0, ios::beg);
  234.  
  235.       do_sync (buf, bypass_pager);
  236.  
  237.       octave_diary << buf;
  238.     }
  239.  
  240.   return 0;
  241. }
  242.  
  243. int
  244. octave_diary_buf::sync (void)
  245. {
  246.   sputc ('\0');
  247.  
  248.   if (write_to_diary_file && external_diary_file)
  249.     external_diary_file << eback ();
  250.  
  251.   seekoff (0, ios::beg);
  252.  
  253.   return 0;
  254. }
  255.  
  256. octave_pager_stream *octave_pager_stream::instance = 0;
  257.  
  258. octave_pager_stream::octave_pager_stream (void) : ostream (), pb (0)
  259. {
  260.   pb = new octave_pager_buf;
  261.   rdbuf (pb);
  262.   setf (unitbuf);
  263. }
  264.  
  265. octave_pager_stream::~octave_pager_stream (void)
  266. {
  267.   flush ();
  268.   delete pb;
  269. }
  270.  
  271. octave_pager_stream&
  272. octave_pager_stream::stream (void)
  273. {
  274.   if (! instance)
  275.     instance = new octave_pager_stream ();
  276.       
  277.   return *instance;
  278. }
  279.  
  280. octave_diary_stream *octave_diary_stream::instance = 0;
  281.  
  282. octave_diary_stream::octave_diary_stream (void) : ostream (), db (0)
  283. {
  284.   db = new octave_diary_buf;
  285.   rdbuf (db);
  286.   setf (unitbuf);
  287. }
  288.  
  289. octave_diary_stream::~octave_diary_stream (void)
  290. {
  291.   flush ();
  292.   delete db;
  293. }
  294.  
  295. octave_diary_stream&
  296. octave_diary_stream::stream (void)
  297. {
  298.   if (! instance)
  299.     instance = new octave_diary_stream ();
  300.  
  301.   return *instance;
  302. }
  303.  
  304. void
  305. flush_octave_stdout (void)
  306. {
  307.   if (! flushing_output_to_pager)
  308.     {
  309.       begin_unwind_frame ("flush_octave_stdout");
  310.  
  311.       unwind_protect_int (really_flush_to_pager);
  312.       unwind_protect_int (flushing_output_to_pager);
  313.  
  314.       really_flush_to_pager = 1;
  315.       flushing_output_to_pager = 1;
  316.  
  317.       octave_stdout.flush ();
  318.  
  319.       if (external_pager)
  320.     clear_external_pager ();
  321.  
  322.       run_unwind_frame ("flush_octave_stdout");
  323.     }
  324. }
  325.  
  326. static void
  327. close_diary_file (void)
  328. {
  329.   if (external_diary_file.is_open ())
  330.     {
  331.       octave_diary.flush ();
  332.       external_diary_file.close ();
  333.     }
  334. }
  335.  
  336. static void
  337. open_diary_file (void)
  338. {
  339.   close_diary_file ();
  340.  
  341.   external_diary_file.open (diary_file.c_str (), ios::app);
  342.  
  343.   if (! external_diary_file)
  344.     error ("diary: can't open diary file `%s'", diary_file.c_str ());
  345. }
  346.  
  347. DEFUN_TEXT (diary, args, ,
  348.   "diary [on|off]\n\
  349. diary [file]\n\
  350. \n\
  351. redirect all input and screen output to a file.")
  352. {
  353.   octave_value_list retval;
  354.  
  355.   int argc = args.length () + 1;
  356.  
  357.   string_vector argv = args.make_argv ("diary");
  358.  
  359.   if (error_state)
  360.     return retval;
  361.  
  362.   if (diary_file.empty ())
  363.     diary_file = "diary";
  364.  
  365.   switch (argc)
  366.     {
  367.     case 1:
  368.       write_to_diary_file = ! write_to_diary_file;
  369.       open_diary_file ();
  370.       break;
  371.  
  372.     case 2:
  373.       {
  374.     string arg = argv[1];
  375.  
  376.     if (arg == "on")
  377.       {
  378.         write_to_diary_file = 1;
  379.         open_diary_file ();
  380.       }    
  381.     else if (arg == "off")
  382.       {
  383.         close_diary_file ();
  384.         write_to_diary_file = 0;
  385.       }
  386.     else
  387.       {
  388.         diary_file = arg;
  389.         open_diary_file ();
  390.       }
  391.       }
  392.       break;
  393.  
  394.     default:
  395.       print_usage ("diary");
  396.       break;
  397.     }
  398.  
  399.   return retval;
  400. }
  401.  
  402. DEFUN_TEXT (more, args, ,
  403.   "more on\n\
  404. more off\n\
  405. \n\
  406. Turn output pagination on or off.")
  407. {
  408.   octave_value_list retval;
  409.  
  410.   int argc = args.length () + 1;
  411.  
  412.   string_vector argv = args.make_argv ("more");
  413.  
  414.   if (error_state)
  415.     return retval;
  416.  
  417.   if (argc == 2)
  418.     {
  419.       string arg = argv[1];
  420.  
  421.       if (arg == "on")
  422.     bind_builtin_variable ("page_screen_output", 1.0);
  423.       else if (arg == "off")
  424.     bind_builtin_variable ("page_screen_output", 0.0);
  425.       else
  426.     error ("more: unrecognized argument `%s'", arg.c_str ());
  427.     }
  428.   else
  429.     print_usage ("more");
  430.  
  431.   return retval;
  432. }
  433.  
  434. static string
  435. default_pager (void)
  436. {
  437.   string pager_binary;
  438.  
  439.   char *pgr = getenv ("PAGER");
  440.  
  441.   if (pgr)
  442.     pager_binary = string (pgr);
  443. #ifdef DEFAULT_PAGER
  444.   else
  445.     {
  446.       pager_binary = string (DEFAULT_PAGER);
  447.  
  448.       if (pager_binary == "less")
  449.     {
  450.       pager_binary.append (" -e");
  451.  
  452.       if (! getenv ("LESS"))
  453.         pager_binary.append
  454.           (" -P'-- less ?pB(%pB\\%):--. (f)orward, (b)ack, (q)uit$'");
  455.     }
  456.     }
  457. #endif
  458.  
  459.   return pager_binary;
  460. }
  461.  
  462. static int
  463. pager_binary (void)
  464. {
  465.   int status = 0;
  466.  
  467.   string s = builtin_string_variable ("PAGER");
  468.  
  469.   if (s.empty ())
  470.     {
  471.       gripe_invalid_value_specified ("PAGER");
  472.       status = -1;
  473.     }
  474.   else
  475.     Vpager_binary = s;
  476.  
  477.   return status;
  478. }
  479.  
  480. static int
  481. page_output_immediately (void)
  482. {
  483.   Vpage_output_immediately = check_preference ("page_output_immediately");
  484.  
  485.   return 0;
  486. }
  487.  
  488. static int
  489. page_screen_output (void)
  490. {
  491.   Vpage_screen_output = check_preference ("page_screen_output");
  492.  
  493.   return 0;
  494. }
  495.  
  496. void
  497. symbols_of_pager (void)
  498. {
  499.   DEFVAR (PAGER, default_pager (), 0, pager_binary,
  500.     "path to pager binary");
  501.  
  502.   DEFVAR (page_output_immediately, 0.0, 0, page_output_immediately,
  503.     "if paging output, start sending it as soon as it is available");
  504.  
  505.   DEFVAR (page_screen_output, 1.0, 0, page_screen_output,
  506.     "if possible, send output intended for the screen through the pager");
  507. }
  508.  
  509. /*
  510. ;;; Local Variables: ***
  511. ;;; mode: C++ ***
  512. ;;; End: ***
  513. */
  514.